{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0PSrErguxVcn"
      },
      "source": [
        "### Brief Description\n",
        "\n",
        "This notebook shows few examples on  how to leverage OpenBB functionality via an Agent built with Langchain.\n",
        "It requires the user to have\n",
        "- an OpenAI Key. This is required as OpenAI is used as LLM\n",
        "\n",
        "\n",
        "For help on how to configure Colab Secrets, please refer to this article\n",
        "https://margaretmz.medium.com/use-colab-secrets-to-store-kaggle-api-key-b57c7464f9fa\n",
        "\n",
        "This work was inspired by examples from this repo https://github.com/AlgoTrading101/Magentic-AlgoTrading101\n",
        "\n",
        "Functionality shown in this notebook is purely an example of what can be done.\n",
        "\n",
        "### Author\n",
        "Marco Mistroni"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Installing dependencies"
      ],
      "metadata": {
        "id": "pw_DrWCXVijQ"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install openbb\n",
        "!pip install openbb-yfinance\n",
        "!pip install openbb-finviz\n",
        "!pip install langchain\n",
        "!pip install langchain_core\n",
        "!pip install langchain_openai\n"
      ],
      "metadata": {
        "id": "sNhm_dyvVlI1"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### Getting keys"
      ],
      "metadata": {
        "id": "Ua8Hmj2lWI6R"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from google.colab import userdata\n",
        "OPENAI_KEY = userdata.get('OPENAI_KEY')"
      ],
      "metadata": {
        "id": "fTUdeQpdWL81"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "TEBnI33fxVcr"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "from openbb import obb\n",
        "from langchain_openai import ChatOpenAI\n",
        "import logging\n",
        "from langchain.agents import tool\n",
        "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
        "\n",
        "llm = ChatOpenAI(model=\"gpt-4.1\", temperature=0, openai_api_key=OPENAI_KEY)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6zNCxSmOxVct"
      },
      "source": [
        "### OpenBB Useful functions"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "28N-T7aIxVcu"
      },
      "outputs": [],
      "source": [
        "@tool\n",
        "def get_industry_performance() -> list:\n",
        "    \"\"\" Return performance by industry for last week, last month, last quarter, last half year and last year\"\"\"\n",
        "    return obb.equity.compare.groups(group='industry', metric='performance').to_llm()\n",
        "\n",
        "@tool\n",
        "def get_strong_buy_for_sector(sector : str) -> list :\n",
        "    \"\"\" Return the strong buy recommendation for a given sector\"\"\"\n",
        "    new_sector = '_'.join(sector.lower().split()).lower()\n",
        "    data = obb.equity.screener(provider='finviz', sector=new_sector, recommendation='buy')\n",
        "    return data.to_llm()\n",
        "\n",
        "@tool\n",
        "def get_strong_buy_for_industry(industry : str) -> list :\n",
        "    \"\"\" Return the strong buy recommendation for a given industry\"\"\"\n",
        "    data = obb.equity.screener(provider='finviz', industry=industry, recommendation='buy')\n",
        "    return data.to_llm()\n",
        "\n",
        "@tool\n",
        "def get_best_stock_performers_for_sector(sector:str) -> list :\n",
        "    \"\"\" Return the best  5 stock performers for last week and last month for a given sector\"\"\"\n",
        "    data = obb.equity.screener(provider='finviz', filters_dict={'Sector' : sector, 'Performance' : 'Week Up', 'Performance 2' : 'Month Up'}, limit=5)\n",
        "    return data.to_llm()\n",
        "\n",
        "@tool\n",
        "def get_best_stock_performers_for_industry(industry:str) -> list :\n",
        "    \"\"\" Return the best  5 stock performers for last week and last month for an industry\"\"\"\n",
        "    data = obb.equity.screener(provider='finviz', filters_dict={'Industry' : industry, 'Performance' : 'Week Up', 'Performance 2' : 'Month Up'}, limit=3)\n",
        "    return data.to_llm()\n",
        "\n",
        "@tool\n",
        "def get_candidate_stocks_to_invest_relaxed(industry:str) -> list:\n",
        "    ''' Use relaxed criteria to find best companies in an industry  which are worth investing into'''\n",
        "    desc_filters = {\n",
        "            'Market Cap.': '+Small (over $300mln)',\n",
        "            'Average Volume': 'Over 200K',\n",
        "        }\n",
        "    fund_filters = {\n",
        "        'InstitutionalOwnership': 'Under 60%',\n",
        "        'Current Ratio' :  'Over 1.5',\n",
        "        'Debt/Equity'   : 'Over 0.3',\n",
        "        #'EPS growthnext 5 years' : 'Positive (>0%)',\n",
        "    }\n",
        "\n",
        "    desc_filters.update(fund_filters)\n",
        "\n",
        "    try:\n",
        "        data = obb.equity.screener(provider='finviz', industry='semiconductors',\n",
        "                    filters_dict=desc_filters\n",
        "                    )\n",
        "        return data.to_llm()\n",
        "    except Exception as e:\n",
        "        logging.info(f'No data found:{str(e)}')\n",
        "        return []\n",
        "\n",
        "\n",
        "@tool\n",
        "def get_valuation_for_industries(input:str) -> list:\n",
        "    \"\"\" Return valuation metrics for the industry provided as input\"\"\"\n",
        "    data =  obb.equity.compare.groups(group='industry', metric='valuation', provider='finviz').to_df()\n",
        "    filtered =  data[data.name == input]\n",
        "    return filtered.to_json(\n",
        "            orient=\"records\",\n",
        "            date_format=\"iso\",\n",
        "            date_unit=\"s\",\n",
        "        )\n",
        "\n",
        "@tool\n",
        "def get_consensus(ticker:str) -> list:\n",
        "    \"\"\" Return analyst consensus for the ticker provided\n",
        "        It returns the following fields:\n",
        "        - target_high: float, High target of the price target consensus.\n",
        "        - target_low: float Low target of the price target consensus.\n",
        "        - target_consensus: float Consensus target of the price target consensus.\n",
        "        - target_median: float Median target of the price target consensus\n",
        "\n",
        "\n",
        "    \"\"\"\n",
        "    data = obb.equity.estimates.consensus(symbol=ticker, limit=3, provider='yfinance').to_df()\n",
        "    return data.to_json(\n",
        "            orient=\"records\",\n",
        "            date_format=\"iso\",\n",
        "            date_unit=\"s\",\n",
        "        )\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1hXQnSGFxVcv"
      },
      "source": [
        "### Chat Memory"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "YBF6S6DxxVcv"
      },
      "outputs": [],
      "source": [
        "from langchain_core.prompts import MessagesPlaceholder\n",
        "from langchain.memory import ConversationTokenBufferMemory\n",
        "from langchain.agents.format_scratchpad.openai_tools import (\n",
        "    format_to_openai_tool_messages,\n",
        ")\n",
        "from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser\n",
        "from langchain_core.output_parsers import StrOutputParser, CommaSeparatedListOutputParser\n",
        "from langchain.agents import AgentExecutor\n",
        "from langchain_core.messages import AIMessage, HumanMessage\n",
        "\n",
        "MEMORY_KEY = \"chat_history\"\n",
        "prompt = ChatPromptTemplate.from_messages(\n",
        "    [\n",
        "        (\n",
        "            \"system\",\n",
        "            \"\"\" You are very powerful stock financial researcher.\n",
        "                You  will take the user questions and answer using the tools available.\n",
        "                Once you have the information you  need, you will answer user's questions using the data returned.\n",
        "                Use the following tools to answer user queries:\n",
        "                - get_strong_buy_for_sector to find strong buy recommendations for a sector\n",
        "                - get_strong_buy_for_industry to find strong buy recommendations for an industry\n",
        "                - get_industry_performance to find the performance for an industry\n",
        "                - get_valuation_for_industries to find valuation metrics for industries\n",
        "                - get_candidate_stocks_to_invest_relaxed to  fetch all companies using relaxed criteria\n",
        "                - def get_consensus(ticker:str) - to find analyst consensus for a company\n",
        "                You should call each function only once, and you should not call the function if you already have the information you need.\n",
        "                \"\"\",\n",
        "        ),\n",
        "        MessagesPlaceholder(variable_name=MEMORY_KEY),\n",
        "        (\"user\", \"{input}\"),\n",
        "        MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n",
        "    ]\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "1yrlv9jUxVcw"
      },
      "outputs": [],
      "source": [
        "tools = [get_industry_performance, get_strong_buy_for_sector, get_strong_buy_for_industry, get_best_stock_performers_for_industry, get_valuation_for_industries,\n",
        "         get_candidate_stocks_to_invest_relaxed, get_consensus]\n",
        "llm_with_tools = llm.bind_tools(tools)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ToVLUWZfxVcw"
      },
      "outputs": [],
      "source": [
        "chat_history = []\n",
        "chat_history.append(HumanMessage(content=\"Your question here\"))\n",
        "chat_history.append(AIMessage(content=\"AI response here\"))\n",
        "memory = ConversationTokenBufferMemory(\n",
        "    llm=llm,  # Required for token counting\n",
        "    max_token_limit=16000,  # Leave buffer for functions + responses\n",
        "    memory_key=\"chat_history\",  # Must match your prompt's key\n",
        "    return_messages=True\n",
        ")\n",
        "\n",
        "agent = (\n",
        "    {\n",
        "        \"input\": lambda x: x[\"input\"],\n",
        "        \"agent_scratchpad\": lambda x: format_to_openai_tool_messages(\n",
        "            x[\"intermediate_steps\"]\n",
        "        ),\n",
        "        \"chat_history\": lambda x: memory.load_memory_variables(x)[\"chat_history\"],\n",
        "    }\n",
        "    | prompt\n",
        "    | llm_with_tools\n",
        "    | OpenAIToolsAgentOutputParser()\n",
        ")\n",
        "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kbFXeyMUxVcy"
      },
      "source": [
        "### Let's try a chain of thought approach. We start with the industry with best performance.\n",
        "Then find the best performing company  and check some metrics"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "MlAvYHU0xVcy"
      },
      "outputs": [],
      "source": [
        "input1 = '''\n",
        "First, find an industry that has consistently shown positive performance across quarterly, monthly, and weekly timeframes.\n",
        "Second, once you have identified the industry, extract its relevant valuation metrics (e.g., P/E, P/B, EV/EBITDA).\n",
        "Third, extract companies from the selected industry using relaxed criteria.\n",
        "Fourth, for the best performing  companies  get the analyst consensus\n",
        "Finally, summarize your findings in no more than 80 words detailing:\n",
        "- Best performing industry\n",
        "- Best performing companies in industry\n",
        "- A table displaying the analyst consensus for each of the companies you found at previous step'''\n",
        "result = agent_executor.invoke({\"input\": input1, \"chat_history\": chat_history})\n",
        "print(result['output'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "au-Qbu9KxVcy"
      },
      "source": [
        "### Finding strong buys in the Utilities sector  "
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "input1 = '''\n",
        "First, find the stocks recommended fro strong buy  in the Utilities Sector\n",
        "Second, find the valuation metrics for this stock.\n",
        "Third, summarize your findings in a short paragraph.\n",
        "'''\n",
        "result = agent_executor.invoke({\"input\": input1, \"chat_history\": chat_history})\n",
        "print(result['output'])"
      ],
      "metadata": {
        "id": "doUw61HPU6fR"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "rc7X4Ep4VNbS"
      },
      "execution_count": null,
      "outputs": []
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "myenv",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.1"
    },
    "colab": {
      "provenance": []
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}